package gamepad;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Hashtable;

import mobilefeel.AndroidDevice;
import mobilefeel.DeviceListener;
import mobilefeel.MobileFeel;
import mobilefeel.NotConnectedException;

import org.flypad.joystick.JoystickException;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;


public class GamePadManager implements DeviceListener {

	private MobileFeel mobileFeel;
	private Controlador controlador;
	private Hashtable<AndroidDevice, GamePad> gamepads;
	
	public GamePadManager(Controlador c) {
		gamepads = new Hashtable<AndroidDevice, GamePad>();
		controlador = c;
		
		mobileFeel = new MobileFeel();
		mobileFeel.addDeviceListener(this);
	}

	public void iniciar(int puerto) throws IOException {
		mobileFeel.listen(puerto);
	}

	public void detener() throws IOException {
		mobileFeel.stopListening();
	}	
	
	@Override
	public void onDeviceConnected(AndroidDevice device) {
		int joystick = controlador.doDispositivoConectado(device);
		
		GamePad gamepad;
		try {
			gamepad = new GamePad(device, joystick);
			gamepads.put(device, gamepad);
			if (new File("gamepad_" + device.getDeviceId() + ".xml").exists())
				leerXml(device, gamepad);
		} catch (JoystickException e) {
			controlador.doErrorCrearJoystick(device);
			try {
				device.disconnect();
			} catch (IOException e1) {}
		}
		controlador.doDispositivoInicializado();
	}
	
	@Override
	public void onDeviceDisconnected(AndroidDevice device) {
		controlador.doDispositivoDesconectado(device);
		
		GamePad gamepad = gamepads.get(device);
		if (gamepad != null) {
			guardarXml(device, gamepad);
			try {
				gamepad.desconectar();
			} catch (JoystickException e) {
				controlador.doErrorConexion();
			} catch (NotConnectedException e) {
				controlador.doErrorConexion();
			} catch (IOException e) {
				controlador.doErrorConexion();
			}
		}
	}
	
	@Override
	public void onCustomDataReceived(AndroidDevice device, String text,
			Serializable object) {
		GamePad gamepad = gamepads.get(device);
		if (gamepad != null)
			try {
				gamepad.customData(text, object);
			} catch (IOException e) {
				controlador.doErrorConexion();
			}
	}

	public void setJoystickNum(AndroidDevice device, int joystick) throws JoystickException {
		GamePad gamepad = gamepads.get(device);
		if (gamepad != null)
			gamepad.setJoystickNum(joystick);
	}

	public GamePad getGamePad(AndroidDevice device) {
		return gamepads.get(device);
	}

	public void salir() throws IOException {
		for (AndroidDevice device : mobileFeel.getDevices()) {
			GamePad gamepad = gamepads.get(device);
			if (gamepad != null) {
				guardarXml(device, gamepad);
				gamepad.desconectar();
			}
		}
		mobileFeel.stopListening();
		mobileFeel.disconnectAll();
	}

	private void guardarXml(AndroidDevice device, GamePad gamepad) {
		Element root = new Element("Device");
		root.setAttribute("id", device.getDeviceId());
		root.addContent(new Element("IntervaloActualizacion").setText(((Integer)gamepad.getUpdateInterval()).toString()));
		root.addContent(new Element("DeadzoneX").setText(((Float)gamepad.getDeadzoneX()).toString()));
		root.addContent(new Element("DeadzoneY").setText(((Float)gamepad.getDeadzoneY()).toString()));
		root.addContent(new Element("LimiteX").setText(((Float)gamepad.getLimiteX()).toString()));
		root.addContent(new Element("LimiteY").setText(((Float)gamepad.getLimiteY()).toString()));
		root.addContent(new Element("AnguloLimite").setText(((Integer)gamepad.getAnguloLimite()).toString()));
		try {
			FileOutputStream output = new FileOutputStream("gamepad_" + device.getDeviceId() + ".xml");
			new XMLOutputter().output(new Document(root), output);
			output.close();
		} catch (FileNotFoundException e) {
			controlador.doErrorGuardarXml();
		} catch (IOException e) {
			controlador.doErrorGuardarXml();
		}
	}
	
	private void leerXml(AndroidDevice device, GamePad gamepad) {
		try {
			SAXBuilder builder = new SAXBuilder ();
			FileInputStream input = new FileInputStream ("gamepad_" + device.getDeviceId() + ".xml");
			Document doc = builder.build (input);
			Element root = doc.getRootElement();
			if (root.getAttribute("id").getValue().equals(device.getDeviceId())) {
				int actualizacion = Integer.parseInt(root.getChildText("IntervaloActualizacion"));
				float deadzoneX = Float.parseFloat(root.getChildText("DeadzoneX"));
				float deadzoneY = Float.parseFloat(root.getChildText("DeadzoneY"));
				float limiteX = Float.parseFloat(root.getChildText("LimiteX"));		
				float limiteY = Float.parseFloat(root.getChildText("LimiteY"));
				int anguloLimite = Integer.parseInt(root.getChildText("AnguloLimite"));
				
				gamepad.setUpdateInterval(actualizacion);
				gamepad.setDeadzones(deadzoneX, deadzoneY);
				gamepad.setLimites(limiteX, limiteY);
				gamepad.setAnguloLimite(anguloLimite);
			} else {
				controlador.doErrorCargarXml();
			}
			input.close();
		} catch (NumberFormatException e) {
			controlador.doErrorCargarXml();
		} catch (FileNotFoundException e) {
			controlador.doErrorCargarXml();
		} catch (JDOMException e) {
			controlador.doErrorCargarXml();
		} catch (IOException e) {
			controlador.doErrorCargarXml();
		}
	}

	public void desconectar(AndroidDevice device) throws IOException {
		device.disconnect();
	}

}
